import json
import time
import decimal
import requests
from quant.markets import Functions, functions
from quant.exchanges.basics import ChannelBasic, HandlerBasic
from quant.exchanges.huobi_util import pong, unzip
from quant.exchanges.util import spot_value_factor, Socket, AscendingChecker, RecentChecker, RecentChecker2
from quant.utils import perf_intv, logging
from quant.const import *


class NullCls:
    def __init__(self, *args, **kwargs):
        pass

    def __getattr__(self, item):
        return self._func

    def _func(self, *args, **kwargs):
        pass


class HuobiSwapUsdtChannel(ChannelBasic):
    def init(self):
        host = ws_host
        self.socket = Socket(host, self.on_open, self.on_message, self.on_close)

    def on_message(self, ws, message):
        now = time.time()
        message = unzip(message)
        if message[2:6] == 'ping':
            return pong(ws, message)
        self.markets.feed_raw(self.event, self.exchange, self.symbol, self.frequency, now, message)

    def subscribe_book(self, ws):
        symbol = self.symbol.replace('/', '-').split('.')[0]
        if self.frequency == DataFrequency.High:
            event = {'sub': 'market.{}.depth.size_150.high_freq'.format(symbol), 'data_type': 'incremental', 'id': '1234'}
        else:
            event = {'sub': 'market.{}.depth.{}'.format(symbol, 'step0'), 'id': '1234'}
        # process_book(freq)不同频率处理不同。增减频率需考虑修改Handler

        message = json.dumps(event)
        ws.send(message)

    def subscribe_trade(self, ws):
        symbol = self.symbol.replace('/', '-').split('.')[0]
        event = {'sub': 'market.{}.trade.detail'.format(symbol), 'id': '1234'}
        message = json.dumps(event)
        ws.send(message)


class HuobiSwapUsdtHandler(HandlerBasic):
    def init(self):
        # self.data_id_checker = RecentChecker(50)  # 首选！
        self.data_id_checker = RecentChecker2(50)  # 首选！

    def process_book(self, routing_key, recv_time, raw):
        if raw == NAME_CHANNEL_CLOSE:
            return self.process_close(routing_key, recv_time)

        data = json.loads(raw)
        if 'subbed' in data:
            return self.info_welcome()

        if 'tick' not in data:
            logging.error('Huobi-swap/usdt channel error: {}'.format(data))
            return

        tick = data['tick']
        server_id = tick['mrid']
        server_time = data['ts'] / 1000
        self.markets.info_engine.push_process_recv(server_id, server_time, recv_time)

        if not self.data_id_checker.is_new(server_id):
            return

        book = self.book
        if self.frequency == DataFrequency.High:
            for p, v in tick['bids']:
                book['buy', float(p)] = float(v)
            for p, v in tick['asks']:
                book['sell', float(p)] = float(v)
            self.check_book(book)

        else:
            book['buy'] = tick['bids']
            book['sell'] = tick['asks']

        self.push_book(routing_key, recv_time, server_time, server_id, book)

    def process_trade(self, routing_key, recv_time, raw):
        if raw == NAME_CHANNEL_CLOSE:
            return self.process_close(routing_key, recv_time)

        data = json.loads(raw)
        if 'subbed' in data:
            return self.info_welcome()

        if 'tick' not in data:
            logging.error('Huobi-swap/usdt channel error: {}'.format(data))
            return

        server_time = data['ts']
        data = data['tick']['data']
        server_id = data[-1]['id']
        self.markets.info_engine.push_process_recv(server_id, server_time, recv_time)

        if not self.data_id_checker.is_new(server_id):
            return

        trade_list = [[d['direction'], float(d['price']), float(d['amount']/2)] for d in data]
        self.push_trade(routing_key, recv_time, server_time, server_id, trade_list)


class HuobiSwapUsdtApi(NullCls):
    pass


class HuobiSwapUsdtSpi(NullCls):
    pass


class HuobiSwapUsdtFunctions(Functions):
    def get_all_ticker(self):
        url = rest_host + '/v2/linear-swap-ex/market/detail/batch_merged'
        response = self.session.get(url, timeout=3)
        if int(response.status_code) // 100 != 2:
            logging.warn('Huobi func-api error: {}'.format(response.text))
            return {}

        data = response.json()
        _usdt = functions.get_asset_value('usdt')
        result = {}

        for d in data['ticks']:
            business_type = d['business_type']
            if business_type != 'swap':
                continue

            symbol = resume_symbol(d['contract_code'])
            if not symbol.endswith('usdt.swap'):
                continue

            if d['bid'] is None:
                continue

            result[symbol] = {
                'price': float(d['close']),
                'vol': float(d['vol']) * _usdt / 2,
                'value': float(d['close']) * _usdt,
                'bid': float(d['bid'][0]),
                'ask': float(d['ask'][0]),
            }

        return result

    def get_value_factor(self, symbol):
        contract_size = functions.get_contract_size('Huobi', symbol)
        asset_value = functions.get_asset_value(symbol.split('/')[0])
        return contract_size * asset_value

    def get_all_contract_size(self):
        path = '/linear-swap-api/v1/swap_contract_info'
        url = rest_host + path
        resp = self.session.get(url, timeout=5)
        j = resp.json()
        result = {}
        for d in j['data']:
            symbol = d['contract_code'].replace('-', '/').lower() + '.swap'
            result[symbol] = d['contract_size']
        return result

    def get_all_precision(self):
        path = '/linear-swap-api/v1/swap_contract_info'
        url = rest_host + path
        resp = self.session.get(url, timeout=5)
        j = resp.json()
        result = {}
        for d in j['data']:
            symbol = d['contract_code'].replace('-', '/').lower() + '.swap'
            price_unit = d['price_tick']
            price_unit = '{:.10f}'.format(price_unit)
            while price_unit[-1] == '0':
                price_unit = price_unit[:-1]
            result[symbol] = (price_unit, '1')
        return result

    def get_recent_trade(self, symbol, _=None):
        url = rest_host + '/linear-swap-ex/market/history/trade'
        param = {
            'contract_code': format_symbol(symbol),
            'size': 2000
        }
        response = self.session.get(url, timeout=3, params=param)

        if int(response.status_code) // 100 != 2:
            logging.warn('Huobi func-api error: {}'.format(response.text))
            return []

        data = response.json()
        if 'data' not in data:
            return []

        result = [
            (
                d['id'],
                d['direction'],
                float(d['price']),
                float(d['quantity']),
                d['ts'] / 1000,
            )
            for dic in data['data'] for d in dic['data']
        ]
        return result


def format_symbol(symbol):
    contract = symbol.split('.')[0]
    contract = contract.replace('/', '-')
    return contract.upper()


def resume_symbol(contract):
    return contract.replace('-', '/').lower() + '.swap'


rest_host = 'https://api.hbdm.vn'
ws_host = 'wss://api.hbdm.vn/linear-swap-ws'


if __name__ == '__main__':
    from quant.markets import Markets
    from quant.utils import set_test_mode
    from utils import *
    set_test_mode()

    def try_channel():
        mar = Markets()
        mar.add_market('Book', 'Huobi', 'matic/usdt.swap', DataFrequency.High)
        mar.add_market('Book', 'Huobi', 'eos/usdt.swap', DataFrequency.Normal)
        mar.add_market('Book', 'Huobi', 'eos/usdt.swap', DataFrequency.Normal)
        mar.add_market('Book', 'Binance', 'eos/usdt.swap', DataFrequency.Normal)
        mar.add_market('Book', 'Okex', 'eos/usdt.swap', DataFrequency.Normal)

        print_trades(mar)
        show_raw_to_data(mar)
        count_raw(mar)
        perf_raw_to_data(mar)

        fun = HuobiSwapUsdtSpotFunctions()
        a = fun.get_all_precision()
        for i in a.items():
            print(i)

    def try_functions():
        fun = HuobiSwapUsdtFunctions()

        # a = fun.get_all_ticker()
        # a = list(a.items())
        # a.sort(key=lambda x: x[1]['vol'], reverse=True)
        # for i in a:
        #     print(i)

        # a = fun.get_all_contract_size()
        # for i in a.items():
        #     print(i)

        # a = functions.get_precision('Huobi', 'shib/usdt.swap')
        # print(a)

        # print(functions.get_value_factor('Huobi', 'btc/usdt.swap'))
        # print(functions.get_value_factor('Huobi', 'eth/usdt.swap'))
        # print(functions.get_value_factor('Huobi', 'xrp/usdt.swap'))

        a = fun.get_recent_trade('core/usdt.swap')
        for i in a:
            print(i)

    try_functions()









